package com.scorpio.sdk.service.impl;

import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.scorpio.common.exception.CustomException;
import com.scorpio.common.utils.DateUtils;
import com.scorpio.common.utils.StringUtils;
import com.scorpio.common.utils.file.FileUploadUtils;
import com.scorpio.common.utils.file.MimeTypeUtils;
import com.scorpio.redis.RedisCache;
import com.scorpio.redis.cachelock.RedisLock;
import com.scorpio.sdk.constant.AliYunConstant;
import com.scorpio.sdk.factory.OssClineFactory;
import com.scorpio.sdk.service.OssService;
import com.scorpio.system.service.ISysConfigService;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;

@Service
public class OssServiceImpl implements OssService {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ISysConfigService configService;

    //外网Cline池子最大数量（外网用于签名）
    @Value("${oss.poolSize}")
    private int poolSize;

    @Value("${oss.poolMinIdle}")
    private int poolMinIdle;

    @Value("${oss.poolMaxIdle}")
    private int poolMaxIdle;

    //内网Cline池子最大数量(内网用于上传)
    @Value("${oss.internalPoolSize}")
    private int internalPoolSize;

    @Value("${oss.internalPoolMinIdle}")
    private int internalPoolMinIdle;

    @Value("${oss.internalPoolMaxIdle}")
    private int internalPoolMaxIdle;


    @Value("${spring.profiles.active}")
    private String environment;

    //oss外网池子
    private GenericObjectPool<OSS> ossObjectPool;

    //oss内网池子
    private GenericObjectPool<OSS> ossInternalObjectPool;

    @PostConstruct
    private void init() {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(poolSize);
        poolConfig.setMinIdle(poolMinIdle);
        poolConfig.setMaxIdle(poolMaxIdle);
        poolConfig.setMaxWaitMillis(10000);
        poolConfig.setLifo(false);
        ossObjectPool = new GenericObjectPool(new OssClineFactory(false), poolConfig);
        poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(internalPoolSize);
        poolConfig.setMinIdle(internalPoolMinIdle);
        poolConfig.setMaxIdle(internalPoolMaxIdle);
        poolConfig.setMaxWaitMillis(10000);
        poolConfig.setLifo(false);
        ossInternalObjectPool = new GenericObjectPool(new OssClineFactory(true), poolConfig);
    }

    //获取存储实例
    @Override
    public String getBucketName() {
        return configService.selectConfigByKey(AliYunConstant.ALIYUN_OSS_BUCKETNAME);
    }

    //获取水印图样式
    @Override
    public String getLogoWatermarkStyle() {
        return configService.selectConfigByKey(AliYunConstant.ALIYUN_OSS_LOGO_WATERMARK_STYLE);
    }

    //获取缩略图样式
    @Override
    public String getHumbnailStyle() {
        return configService.selectConfigByKey(AliYunConstant.ALIYUN_OSS_THUMBNAIL_STYLE);
    }

    @Override
    public String getFenghaoStyle() {
        return configService.selectConfigByKey(AliYunConstant.ALIYUN_OSS_FENGHAO_STYLE);
    }

    @Override
    public String getAvatarStyle() {
        return configService.selectConfigByKey(AliYunConstant.ALIYUN_OSS_AVATAR_STYLE);
    }


    //上传图片
    @Override
    public String putImage(MultipartFile multipartFile) {
        GenericObjectPool<OSS> pool = "local".equals(environment) ? ossObjectPool : ossInternalObjectPool;
        OSS oss = null;
        try {
            oss = pool.borrowObject();

            FileUploadUtils.assertAllowed(multipartFile, MimeTypeUtils.IMAGE_EXTENSION);
            String extension = FileUploadUtils.getExtension(multipartFile);

            String filePath = AliYunConstant.getFilePath(AliYunConstant.getOssImagePath(), extension);
            oss.putObject(getBucketName(), filePath, multipartFile.getInputStream());
            return filePath;
        } catch (Exception e) {
            throw new CustomException("系统出错");
        } finally {
            if (oss != null) {
                pool.returnObject(oss);
            }
        }
    }

    //上传音频
    @Override
    public String putAudio(MultipartFile multipartFile) {
        GenericObjectPool<OSS> pool = "local".equals(environment) ? ossObjectPool : ossInternalObjectPool;
        OSS oss = null;
        try {
            oss = pool.borrowObject();
            FileUploadUtils.assertAllowed(multipartFile, MimeTypeUtils.VIDEO_TYPE_AUDIO);
            String extension = FileUploadUtils.getExtension(multipartFile);

            String filePath = AliYunConstant.getFilePath(AliYunConstant.getOssAudioPath(), extension);
            oss.putObject(getBucketName(), filePath, multipartFile.getInputStream());
            return filePath;
        } catch (Exception e) {
            throw new CustomException("系统出错");
        } finally {
            if (oss != null) {
                pool.returnObject(oss);
            }
        }
    }

    //上传视频
    @Override
    public String putVideo(MultipartFile multipartFile) {
        GenericObjectPool<OSS> pool = "local".equals(environment) ? ossObjectPool : ossInternalObjectPool;
        OSS oss = null;
        try {
            oss = pool.borrowObject();
            FileUploadUtils.assertAllowed(multipartFile, MimeTypeUtils.VIDEO_EXTENSION);
            String extension = FileUploadUtils.getExtension(multipartFile);

            String filePath = AliYunConstant.getFilePath(AliYunConstant.getOssVideoPath(), extension);
            oss.putObject(getBucketName(), filePath, multipartFile.getInputStream());
            return filePath;
        } catch (Exception e) {
            throw new CustomException("系统出错");
        } finally {
            if (oss != null) {
                pool.returnObject(oss);
            }
        }
    }

    //上传IM图片
    @Override
    public String putImImage(MultipartFile multipartFile) {
        GenericObjectPool<OSS> pool = "local".equals(environment) ? ossObjectPool : ossInternalObjectPool;
        OSS oss = null;
        try {
            oss = pool.borrowObject();
            FileUploadUtils.assertAllowed(multipartFile, MimeTypeUtils.IMAGE_EXTENSION);
            String extension = FileUploadUtils.getExtension(multipartFile);

            String filePath = AliYunConstant.getImImagePath(extension);
            oss.putObject(getBucketName(), filePath, multipartFile.getInputStream());
            return filePath;
        } catch (Exception e) {
            throw new CustomException("系统出错");
        } finally {
            if (oss != null) {
                pool.returnObject(oss);
            }
        }
    }

    //上传对象
    @Override
    public String putObject(String filePath, InputStream is) {
        return putObject(getBucketName(), filePath, is);
    }

    @Override
    public String putObject(String filePath, InputStream is, CannedAccessControlList objectAcl) {
        return putObject(getBucketName(), filePath, is, objectAcl);
    }

    @Override
    public String putObject(String filePath, File file, CannedAccessControlList objectAcl) {
        String bucketName = getBucketName();
        GenericObjectPool<OSS> pool = "local".equals(environment) ? ossObjectPool : ossInternalObjectPool;
        OSS oss = null;
        try {
            oss = pool.borrowObject();
            if (objectAcl != null && objectAcl == CannedAccessControlList.Private) {
                filePath = AliYunConstant.getPrivatePath() + filePath;
            }
            oss.putObject(bucketName, filePath, file);
            if (objectAcl == null) {
                objectAcl = CannedAccessControlList.PublicRead;
            }
            oss.setObjectAcl(bucketName, filePath, objectAcl);
            return filePath;
        } catch (Exception e) {
            throw new CustomException("系统出错");
        } finally {
            if (oss != null) {
                pool.returnObject(oss);
            }
        }
    }

    @Override
    public String putObject(String bucketName, String filePath, InputStream is) {
        return putObject(bucketName, filePath, is, CannedAccessControlList.Default);
    }

    @Override
    public String putObject(String bucketName, String filePath, InputStream is, CannedAccessControlList objectAcl) {
        GenericObjectPool<OSS> pool = "local".equals(environment) ? ossObjectPool : ossInternalObjectPool;
        OSS oss = null;
        try {
            oss = pool.borrowObject();
            if (objectAcl != null && objectAcl == CannedAccessControlList.Private) {
                filePath = AliYunConstant.getPrivatePath() + filePath;
            }
            oss.putObject(bucketName, filePath, is);
            if (objectAcl != null) {
                oss.setObjectAcl(bucketName, filePath, objectAcl);
            }
            return filePath;
        } catch (Exception e) {
            throw new CustomException("系统出错");
        } finally {
            if (oss != null) {
                pool.returnObject(oss);
            }
        }
    }

    @Override
    public String putObject(String filePath, File file) {
        GenericObjectPool<OSS> pool = "local".equals(environment) ? ossObjectPool : ossInternalObjectPool;
        OSS oss = null;
        try {
            oss = pool.borrowObject();
            oss.putObject(getBucketName(), filePath, file);
            return filePath;
        } catch (Exception e) {
            throw new CustomException("系统出错");
        } finally {
            if (oss != null) {
                pool.returnObject(oss);
            }
        }
    }

    //删除文件
    @Override
    public void deleteObject(String filePath) {
        if (StringUtils.isEmpty(filePath)) return;
        if ("local".equals(environment)) return;
        GenericObjectPool<OSS> pool = "local".equals(environment) ? ossObjectPool : ossInternalObjectPool;
        OSS oss = null;
        try {
            oss = pool.borrowObject();
            oss.deleteObject(getBucketName(), filePath);
        } catch (Exception e) {
            throw new CustomException("删除OSS文件异常:" + filePath);
        } finally {
            if (oss != null) {
                pool.returnObject(oss);
            }
        }
    }

    /**
     * 获取水印图样式
     *
     * @param filePath
     * @return
     */
    @Override
    public String getLogoWatermarkImageUrl(String filePath) {
        return getCertificateUrl(filePath, getLogoWatermarkStyle());
    }

    /**
     * 获取缩略图样式
     *
     * @param filePath
     * @return
     */
    @Override
    public String getHumbnailImageUrl(String filePath) {
        return getCertificateUrl(filePath, getHumbnailStyle());
    }

    @Override
    public String getFenghaoImageUrl(String filePath) {
        return getCertificateUrl(filePath, getFenghaoStyle());
    }

    @Override
    public String getAvatarImageUrl(String filePath) {
        return getCertificateUrl(filePath, getAvatarStyle());
    }

    @Override
    public String getCertificateUrl(String filePath) {
        return getCertificateUrl(filePath, null);
    }


    @Override
    public String getCertificateUrl(String filePath, String style) {
        return getCertificateUrl(filePath, style, 0);
    }


    /**
     * 获取证书地址
     *
     * @param filePath       存储路径
     * @param style          图片签名样式
     * @param expirationTime 图片路径过期时间/秒
     * @return
     */
    @Override
    public String getCertificateUrl(String filePath, String style, long expirationTime) {
        OSS oss = null;
        try {
            oss = ossObjectPool.borrowObject();
            GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(getBucketName(), filePath, HttpMethod.GET);
            if (expirationTime > 0) {
                Date expiration = new Date(new Date().getTime() + (expirationTime * 1000));
                if (expirationTime > 0l) {
                    req.setExpiration(expiration);
                } else {
                    req.setExpiration(new Date(System.currentTimeMillis() + (DateUtils.MILLIS_PER_DAY * 365 * 5)));
                }
            } else {
                StringBuffer sb = new StringBuffer();
                sb.append(AliYunConstant.PUBLIC_URL.replace("#{bucketName}", getBucketName()).replace("#{path}", filePath));
                if (!StringUtils.isEmpty(style)) {
                    sb.append("?x-oss-process=");
                    sb.append(style);
                }
                return sb.toString();
            }
            if (!StringUtils.isEmpty(style)) req.setProcess(style);
            URL signedUrl = oss.generatePresignedUrl(req);
            String url = signedUrl.toString();
            if (!url.startsWith("https://")) {
                url = url.replace("http://", "https://");
            }
            return url;
        } catch (Exception e) {
            throw new CustomException("系统出错", e);
        } finally {
            if (oss != null) {
                ossObjectPool.returnObject(oss);
            }
        }
    }

    @Override
    public void setObjectAcl(String filePath, CannedAccessControlList objectAcl) {
        OSS oss = null;
        try {
            oss = ossObjectPool.borrowObject();
            oss.setObjectAcl(getBucketName(), filePath, objectAcl);
        } catch (Exception e) {
            throw new CustomException("系统出错", e);
        } finally {
            if (oss != null) {
                ossObjectPool.returnObject(oss);
            }
        }
    }

    @Override
    public String getPeivateCertificateUrl(String filePath) {
        if (filePath.startsWith("http")) {
            return filePath;
        }
        if (!filePath.startsWith(AliYunConstant.getPrivatePath())) {
            filePath = AliYunConstant.getPrivatePath() + filePath;
        }
        return getCertificateUrl(filePath, null, (DateUtils.MILLIS_PER_DAY / 1000) * 7);
    }

    @Override
    public void clanCache() {
        ossObjectPool.clear();
        ossInternalObjectPool.clear();
    }

}
